<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>工作流程编辑器 (面向对象版本)</title>
    <script src="js/react.production.min.js"></script>
    <script src="js/react-dom.production.min.js"></script>
    <script src="js/babel.min.js"></script>
    <script src="js/reactflow.js"></script>
    <style>
        body, html {
            margin: 0;
            padding: 0;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
            height: 100%;
        }
        #root {
            height: 100vh;
        }
        .workflow-container {
            height: 100vh;
            display: flex;
            flex-direction: column;
        }
        .react-flow__edge {
            cursor: pointer;
        }
        .react-flow__edge:hover {
            stroke-width: 3px;
        }
    </style>
</head>
<body>
    <div id="root"></div>

    <script type="text/babel">
        const { useState, useCallback, useEffect, useMemo } = React;
        const { ReactFlow, addEdge, useNodesState, useEdgesState, Handle, Position, Background, Controls, MiniMap, getSmoothStepPath } = ReactFlow;

        /**
         * 常量配置类
         */
        class Constants {
            constructor() {
                this.NODE_COLORS = {
                    start: '#52c41a',
                    end: '#ff4d4f',
                    condition: '#fa8c16',
                    normal: '#1890ff'
                };

                this.NODE_STYLES = {
                    start: {
                        background: 'linear-gradient(135deg, #f6ffed 0%, #d9f7be 100%)',
                        border: '2px solid #52c41a',
                        borderRadius: '20px',
                        color: '#135200',
                        fontWeight: '600'
                    },
                    end: {
                        background: 'linear-gradient(135deg, #fff2f0 0%, #ffccc7 100%)',
                        border: '2px solid #ff4d4f',
                        borderRadius: '20px',
                        color: '#a8071a',
                        fontWeight: '600'
                    },
                    condition: {
                        background: 'linear-gradient(135deg, #fff7e6 0%, #ffd591 100%)',
                        border: '2px solid #fa8c16',
                        borderRadius: '0',
                        transform: 'rotate(45deg)',
                        color: '#ad4e00',
                        fontWeight: '600'
                    },
                    normal: {
                        background: 'linear-gradient(135deg, #e6f7ff 0%, #bae7ff 100%)',
                        border: '2px solid #1890ff',
                        borderRadius: '6px',
                        color: '#0050b3',
                        fontWeight: 'normal'
                    }
                };

                this.STORAGE_KEY = 'workflow-data-oop';
            }
        }

        /**
         * 工具类 - 提供通用工具方法
         */
        class Utils {
            constructor() {
                this.skillNodePattern = /skill/i;
                this.ctrlNodePattern = /^ctrl/i;
            }

            generateId() {
                return `node_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
            }

            isSkillNode(cmd) {
                return cmd && this.skillNodePattern.test(cmd);
            }

            isCtrlNode(cmd) {
                return cmd && this.ctrlNodePattern.test(cmd);
            }

            getNodeType(name) {
                if (!name) return 'normal';
                const lowerName = name.toLowerCase();
                
                if (lowerName.includes('开始') || lowerName.includes('start')) {
                    return 'start';
                } else if (lowerName.includes('结束') || lowerName.includes('end')) {
                    return 'end';
                } else if (lowerName.includes('判断') || lowerName.includes('条件') || lowerName.includes('condition')) {
                    return 'condition';
                }
                return 'normal';
            }

            deepClone(obj) {
                return JSON.parse(JSON.stringify(obj));
            }

            formatDate(date) {
                return new Date(date).toISOString().slice(0, 19).replace(/:/g, '-');
            }
        }

        /**
         * 节点管理器类
         */
        class NodeManager {
            constructor() {
                this.constants = new Constants();
                this.utils = new Utils();
                this.eventListeners = {};
            }

            createNode(cmdData, position = { x: 0, y: 0 }) {
                const nodeId = this.utils.generateId();
                const nodeType = this.utils.getNodeType(cmdData.Name);
                
                return {
                    id: nodeId,
                    type: 'custom',
                    position,
                    data: {
                        ...cmdData,
                        'data-node-type': nodeType,
                        style: this.constants.NODE_STYLES[nodeType]
                    }
                };
            }

            applyNodeStyling(node) {
                const nodeType = node.data['data-node-type'] || 'normal';
                return {
                    ...node,
                    data: {
                        ...node.data,
                        style: {
                            ...this.constants.NODE_STYLES[nodeType],
                            ...node.data.style
                        }
                    }
                };
            }

            updateNodeData(nodeId, newData) {
                this.emit('node-update', { nodeId, newData });
            }

            addEventListener(event, callback) {
                if (!this.eventListeners[event]) {
                    this.eventListeners[event] = [];
                }
                this.eventListeners[event].push(callback);
            }

            emit(event, data) {
                if (this.eventListeners[event]) {
                    this.eventListeners[event].forEach(callback => callback(data));
                }
            }
        }

        /**
         * 边管理器类
         */
        class EdgeManager {
            constructor() {
                this.utils = new Utils();
                this.eventListeners = {};
            }

            createEdge(params) {
                const edgeId = `edge_${this.utils.generateId()}`;
                return {
                    id: edgeId,
                    type: 'smoothstep',
                    source: params.source,
                    target: params.target,
                    sourceHandle: params.sourceHandle,
                    targetHandle: params.targetHandle,
                    style: {
                        stroke: '#1890ff',
                        strokeWidth: 2
                    },
                    markerEnd: {
                        type: 'arrowclosed',
                        color: '#1890ff'
                    }
                };
            }

            updateEdgeStyle(edgeId, newStyle) {
                this.emit('edge-update', { edgeId, newStyle });
            }

            addEventListener(event, callback) {
                if (!this.eventListeners[event]) {
                    this.eventListeners[event] = [];
                }
                this.eventListeners[event].push(callback);
            }

            emit(event, data) {
                if (this.eventListeners[event]) {
                    this.eventListeners[event].forEach(callback => callback(data));
                }
            }
        }

        /**
         * 流程数据管理器类
         */
        class FlowDataManager {
            constructor() {
                this.nodeManager = new NodeManager();
                this.utils = new Utils();
            }

            convertToReactFlow(data, isSubFlow = false) {
                const nodes = [];
                const edges = [];
                const nodeMap = new Map();

                if (!data?.Cmds) {
                    console.warn('数据格式不正确，缺少 Cmds 数组');
                    return { nodes: [], edges: [] };
                }

                // 创建节点
                data.Cmds.forEach((cmd, index) => {
                    const position = cmd.position || {
                        x: 200 + (index % 4) * 200,
                        y: 100 + Math.floor(index / 4) * 150
                    };

                    const node = this.nodeManager.createNode(cmd, position);
                    nodes.push(node);
                    nodeMap.set(cmd.Name, node.id);
                });

                // 创建连接线
                data.Cmds.forEach(cmd => {
                    if (cmd.Args) {
                        Object.entries(cmd.Args).forEach(([key, targetName]) => {
                            if (key.startsWith('Next') && targetName && nodeMap.has(targetName)) {
                                const sourceNodeId = nodeMap.get(cmd.Name);
                                const targetNodeId = nodeMap.get(targetName);
                                
                                if (sourceNodeId && targetNodeId) {
                                    const edgeId = `edge_${sourceNodeId}_${targetNodeId}_${key}`;
                                    
                                    // 判断是否为条件节点的不同输出
                                    let sourceHandle = 'output-main';
                                    if (cmd.Name.includes('判断') || cmd.Name.includes('条件')) {
                                        if (key === 'Next.1' || key.includes('false') || key.includes('fail')) {
                                            sourceHandle = 'output-fail';
                                        }
                                    }

                                    edges.push({
                                        id: edgeId,
                                        source: sourceNodeId,
                                        target: targetNodeId,
                                        sourceHandle: sourceHandle,
                                        type: 'smoothstep',
                                        style: {
                                            stroke: sourceHandle === 'output-fail' ? '#ff4d4f' : '#1890ff',
                                            strokeWidth: 2
                                        },
                                        markerEnd: {
                                            type: 'arrowclosed',
                                            color: sourceHandle === 'output-fail' ? '#ff4d4f' : '#1890ff'
                                        },
                                        data: {
                                            label: key.replace('Next.', '分支')
                                        }
                                    });
                                }
                            }
                        });
                    }
                });

                return { nodes, edges };
            }

            convertFromReactFlow(nodes, edges) {
                const config = {
                    Name: "工作流程",
                    Cmds: []
                };

                const nodeMap = new Map();
                nodes.forEach(node => {
                    nodeMap.set(node.id, node);
                });

                // 转换节点
                nodes.forEach(node => {
                    if (node.data) {
                        const cmdObj = {
                            Name: node.data.Name || '',
                            Args: {},
                            position: node.position
                        };

                        if (node.data.Cmd) {
                            cmdObj.Cmd = node.data.Cmd;
                        }

                        if (node.data.InParams && Object.keys(node.data.InParams).length > 0) {
                            cmdObj.InParams = node.data.InParams;
                        }

                        if (node.data.OutParams && node.data.OutParams.length > 0) {
                            cmdObj.OutParams = node.data.OutParams;
                        }
                        
                        if (node.data.AlreadyParse !== undefined) {
                            cmdObj.AlreadyParse = node.data.AlreadyParse;
                        }
                        
                        config.Cmds.push(cmdObj);
                    }
                });

                // 转换连接线为Args
                edges.forEach(edge => {
                    const sourceNode = nodeMap.get(edge.source);
                    const targetNode = nodeMap.get(edge.target);
                    
                    if (sourceNode && targetNode) {
                        const sourceCmdIndex = config.Cmds.findIndex(cmd => cmd.Name === sourceNode.data.Name);
                        if (sourceCmdIndex !== -1) {
                            let argKey = 'Next';
                            if (edge.sourceHandle === 'output-fail') {
                                argKey = 'Next.1';
                            } else {
                                // 计算已有的Next参数数量
                                const existingNextKeys = Object.keys(config.Cmds[sourceCmdIndex].Args || {})
                                    .filter(key => key.startsWith('Next'));
                                argKey = existingNextKeys.length > 0 ? `Next.${existingNextKeys.length}` : 'Next';
                            }
                            
                            if (!config.Cmds[sourceCmdIndex].Args) {
                                config.Cmds[sourceCmdIndex].Args = {};
                            }
                            
                            config.Cmds[sourceCmdIndex].Args[argKey] = targetNode.data.Name;
                        }
                    }
                });

                return config;
            }
        }

        /**
         * 自定义节点组件
         */
        const CustomNode = ({ data, selected }) => {
            const nodeType = data['data-node-type'] || 'normal';
            const isCondition = nodeType === 'condition';
            const isStart = nodeType === 'start';
            const isEnd = nodeType === 'end';
            const isSubFlow = data.Args?.Type === 'SubFlow';
            const utils = useMemo(() => new Utils(), []);
            const isSkillNode = utils.isSkillNode(data.Cmd);
            const isCtrlNode = utils.isCtrlNode(data.Cmd);

            return (
                <div 
                    style={{
                        ...data.style,
                        position: 'relative',
                        minWidth: isCondition ? '100px' : '120px',
                        minHeight: isCondition ? '100px' : (isSubFlow ? '60px' : '40px'),
                        display: 'flex',
                        alignItems: 'center',
                        justifyContent: 'center',
                        textAlign: 'center',
                        cursor: 'pointer',
                        border: isSubFlow ? '2px dashed #1890ff' : data.style?.border,
                        background: isSubFlow 
                            ? 'linear-gradient(135deg, #e6f7ff 0%, #bae7ff 100%)' 
                            : data.style?.background,
                        transition: isSubFlow ? 'all 0.3s ease' : 'none'
                    }}
                >
                    {/* SubFlow 图标标识 */}
                    {isSubFlow && (
                        <div 
                            style={{
                                position: 'absolute',
                                top: '-8px',
                                right: '-8px',
                                width: '20px',
                                height: '20px',
                                background: 'linear-gradient(135deg, #1890ff 0%, #096dd9 100%)',
                                borderRadius: '50%',
                                display: 'flex',
                                alignItems: 'center',
                                justifyContent: 'center',
                                fontSize: '10px',
                                color: 'white',
                                fontWeight: 'bold',
                                border: '2px solid white',
                                boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
                                zIndex: 10
                            }}
                            title={`子流程节点\n文件名: ${data.Cmd}\n类型: ${data.Args?.Type}`}
                        >
                            ⚙️
                        </div>
                    )}
                    
                    {/* Skill 节点标识 */}
                    {isSkillNode && (
                        <div 
                            style={{
                                position: 'absolute',
                                top: '-8px',
                                left: '-8px',
                                width: '18px',
                                height: '18px',
                                background: 'linear-gradient(135deg, #722ed1 0%, #531dab 100%)',
                                borderRadius: '4px',
                                display: 'flex',
                                alignItems: 'center',
                                justifyContent: 'center',
                                fontSize: '9px',
                                color: 'white',
                                fontWeight: 'bold',
                                border: '2px solid white',
                                boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
                                zIndex: 9
                            }}
                            title={`Skill节点\n位于skill文件夹\n文件名: ${data.Cmd}`}
                        >
                            S
                        </div>
                    )}
                    
                    {/* Ctrl 节点标识 */}
                    {isCtrlNode && (
                        <div 
                            style={{
                                position: 'absolute',
                                top: '-8px',
                                left: isSkillNode ? '12px' : '-8px',
                                width: '18px',
                                height: '18px',
                                background: 'linear-gradient(135deg, #ff7a45 0%, #fa541c 100%)',
                                borderRadius: '4px',
                                display: 'flex',
                                alignItems: 'center',
                                justifyContent: 'center',
                                fontSize: '9px',
                                color: 'white',
                                fontWeight: 'bold',
                                border: '2px solid white',
                                boxShadow: '0 2px 4px rgba(0,0,0,0.2)',
                                zIndex: 8
                            }}
                            title={`控制节点\n位于ctrl文件夹\n文件名: ${data.Cmd}`}
                        >
                            C
                        </div>
                    )}

                    {/* 输入连接点 */}
                    {!isStart && (
                        <Handle
                            type="target"
                            position={Position.Left}
                            style={{
                                background: '#ff4d4f',
                                border: '2px solid white',
                                width: '10px',
                                height: '10px',
                                left: '-5px'
                            }}
                        />
                    )}

                    {/* 节点内容 */}
                    <div style={{ 
                        transform: isCondition ? 'rotate(-45deg)' : 'none',
                        maxWidth: isCondition ? '80px' : '100%',
                        wordBreak: 'break-word',
                        fontSize: isSubFlow ? '11px' : '12px',
                        lineHeight: '1.2',
                        padding: isSubFlow ? '4px' : '0'
                    }}>
                        <div style={{ 
                            fontWeight: isSubFlow ? '600' : 'normal',
                            color: isSubFlow ? '#1890ff' : 'inherit'
                        }}>
                            {data.Name}
                        </div>
                        
                        {isSubFlow && data.Cmd && (
                            <div style={{ 
                                fontSize: '9px', 
                                color: '#1890ff',
                                background: 'rgba(24, 144, 255, 0.1)',
                                padding: '1px 4px',
                                borderRadius: '3px',
                                marginTop: '2px',
                                fontWeight: '500'
                            }}>
                                📄 {data.Cmd}
                            </div>
                        )}
                        
                        {!isSubFlow && data.Cmd && data.Cmd !== data.Name && (
                            <div style={{ 
                                fontSize: '10px', 
                                opacity: 0.8, 
                                marginTop: '2px' 
                            }}>
                                {data.Cmd}
                            </div>
                        )}
                    </div>

                    {/* 输出连接点 */}
                    {!isEnd && (
                        <>
                            <Handle
                                type="source"
                                position={Position.Right}
                                id="output-main"
                                style={{
                                    background: '#52c41a',
                                    border: '2px solid white',
                                    width: '10px',
                                    height: '10px',
                                    right: '-5px',
                                    top: '50%'
                                }}
                            />
                            
                            {/* 条件节点的第二个输出点 */}
                            {isCondition && (
                                <Handle
                                    type="source"
                                    position={Position.Bottom}
                                    id="output-fail"
                                    style={{
                                        background: '#ff4d4f',
                                        border: '2px solid white',
                                        width: '10px',
                                        height: '10px',
                                        bottom: '-5px',
                                        left: '50%',
                                        transform: 'translateX(-50%)'
                                    }}
                                />
                            )}
                        </>
                    )}
                </div>
            );
        };

        const nodeTypes = {
            custom: CustomNode
        };

        /**
         * UI管理器类
         */
        class UIManager {
            constructor() {
                this.eventListeners = {};
                this.init();
            }

            init() {
                this.setupGlobalStyles();
            }

            setupGlobalStyles() {
                if (!document.getElementById('workflow-styles')) {
                    const style = document.createElement('style');
                    style.id = 'workflow-styles';
                    style.textContent = `
                        .workflow-container {
                            height: 100vh;
                            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
                        }
                    `;
                    document.head.appendChild(style);
                }
            }

            showMessage(type, content, duration = 3000) {
                const messageContainer = this.getOrCreateMessageContainer();
                const messageElement = this.createMessageElement(type, content);
                
                messageContainer.appendChild(messageElement);
                
                setTimeout(() => {
                    if (messageElement.parentNode) {
                        messageElement.parentNode.removeChild(messageElement);
                    }
                }, duration);
            }

            getOrCreateMessageContainer() {
                let container = document.getElementById('message-container');
                if (!container) {
                    container = document.createElement('div');
                    container.id = 'message-container';
                    container.style.cssText = `
                        position: fixed;
                        top: 20px;
                        right: 20px;
                        z-index: 9999;
                        pointer-events: none;
                    `;
                    document.body.appendChild(container);
                }
                return container;
            }

            createMessageElement(type, content) {
                const colors = {
                    success: '#52c41a',
                    error: '#ff4d4f',
                    warning: '#faad14',
                    info: '#1890ff'
                };

                const element = document.createElement('div');
                element.style.cssText = `
                    background: white;
                    padding: 12px 16px;
                    margin-bottom: 8px;
                    border-radius: 6px;
                    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
                    border-left: 4px solid ${colors[type] || colors.info};
                    pointer-events: auto;
                    max-width: 300px;
                    animation: slideInRight 0.3s ease;
                `;
                element.textContent = content;

                if (!document.getElementById('message-animations')) {
                    const style = document.createElement('style');
                    style.id = 'message-animations';
                    style.textContent = `
                        @keyframes slideInRight {
                            from { transform: translateX(100%); opacity: 0; }
                            to { transform: translateX(0); opacity: 1; }
                        }
                    `;
                    document.head.appendChild(style);
                }

                return element;
            }

            addEventListener(event, callback) {
                if (!this.eventListeners[event]) {
                    this.eventListeners[event] = [];
                }
                this.eventListeners[event].push(callback);
            }

            emit(event, data) {
                if (this.eventListeners[event]) {
                    this.eventListeners[event].forEach(callback => callback(data));
                }
            }
        }

        /**
         * SubFlow管理器类
         */
        class SubFlowManager {
            constructor(uiManager) {
                this.uiManager = uiManager;
                this.subFlowCache = new Map();
                this.eventListeners = {};
            }

            async loadSubFlow(filename) {
                if (this.subFlowCache.has(filename)) {
                    return this.subFlowCache.get(filename);
                }

                try {
                    const response = await fetch(`services/${filename}`);
                    if (!response.ok) {
                        throw new Error(`HTTP error! status: ${response.status}`);
                    }
                    
                    const data = await response.json();
                    this.subFlowCache.set(filename, data);
                    return data;
                } catch (error) {
                    console.error(`加载子流程失败: ${filename}`, error);
                    this.uiManager?.showMessage('error', `加载子流程失败: ${filename}`);
                    return null;
                }
            }

            async handleSubFlowClick(nodeData) {
                if (!nodeData.Cmd) {
                    this.uiManager?.showMessage('warning', '子流程文件名不存在');
                    return;
                }

                const subFlowData = await this.loadSubFlow(nodeData.Cmd);
                if (subFlowData) {
                    this.emit('subflow-view', { data: subFlowData, node: nodeData });
                }
            }

            addEventListener(event, callback) {
                if (!this.eventListeners[event]) {
                    this.eventListeners[event] = [];
                }
                this.eventListeners[event].push(callback);
            }

            emit(event, data) {
                if (this.eventListeners[event]) {
                    this.eventListeners[event].forEach(callback => callback(data));
                }
            }
        }

        /**
         * 工作流应用主类
         */
        class WorkflowApp {
            constructor() {
                this.constants = new Constants();
                this.utils = new Utils();
                this.nodeManager = new NodeManager();
                this.edgeManager = new EdgeManager();
                this.flowDataManager = new FlowDataManager();
                this.uiManager = new UIManager();
                this.subFlowManager = new SubFlowManager(this.uiManager);
                
                this.currentData = null;
                this.setupEventListeners();
            }

            setupEventListeners() {
                this.subFlowManager.addEventListener('subflow-view', (eventData) => {
                    this.handleSubFlowView(eventData);
                });
            }

            handleSubFlowView(eventData) {
                this.uiManager.emit('show-subflow-modal', eventData);
            }

            async loadData(data) {
                try {
                    this.currentData = data;
                    const result = this.flowDataManager.convertToReactFlow(data);
                    
                    result.nodes = result.nodes.map(node => 
                        this.nodeManager.applyNodeStyling(node)
                    );
                    
                    return result;
                } catch (error) {
                    console.error('加载数据失败:', error);
                    this.uiManager.showMessage('error', '数据加载失败');
                    throw error;
                }
            }

            saveToLocalStorage(data) {
                try {
                    localStorage.setItem(this.constants.STORAGE_KEY, JSON.stringify(data));
                    this.uiManager.showMessage('success', '数据已保存到本地存储');
                } catch (error) {
                    console.error('保存到本地存储失败:', error);
                    this.uiManager.showMessage('error', '保存失败');
                }
            }

            loadFromLocalStorage() {
                try {
                    const data = localStorage.getItem(this.constants.STORAGE_KEY);
                    return data ? JSON.parse(data) : null;
                } catch (error) {
                    console.error('从本地存储加载失败:', error);
                    return null;
                }
            }

            exportData() {
                if (!this.currentData) {
                    this.uiManager.showMessage('warning', '没有可导出的数据');
                    return;
                }

                const exportData = this.flowDataManager.convertFromReactFlow(
                    this.currentData.nodes,
                    this.currentData.edges
                );

                const blob = new Blob([JSON.stringify(exportData, null, 2)], {
                    type: 'application/json'
                });
                
                const url = URL.createObjectURL(blob);
                const link = document.createElement('a');
                link.href = url;
                link.download = `workflow-${this.utils.formatDate(new Date())}.json`;
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
                URL.revokeObjectURL(url);
                
                this.uiManager.showMessage('success', '数据导出成功');
            }
        }

        /**
         * SubFlow查看器组件
         */
        const SubFlowViewer = ({ data }) => {
            const [subNodes, setSubNodes] = useState([]);
            const [subEdges, setSubEdges] = useState([]);
            const flowDataManager = useMemo(() => new FlowDataManager(), []);
            
            useEffect(() => {
                if (data) {
                    try {
                        const { nodes, edges } = flowDataManager.convertToReactFlow(data, true);
                        setSubNodes(nodes);
                        setSubEdges(edges);
                    } catch (error) {
                        console.error('转换SubFlow数据失败:', error);
                    }
                }
            }, [data, flowDataManager]);

            return (
                <div style={{ width: '100%', height: '100%', position: 'relative' }}>
                    <ReactFlow
                        nodes={subNodes}
                        edges={subEdges}
                        nodeTypes={nodeTypes}
                        fitView
                        fitViewOptions={{ padding: 0.2 }}
                        defaultViewport={{ x: 0, y: 0, zoom: 0.8 }}
                        nodesDraggable={false}
                        nodesConnectable={false}
                        elementsSelectable={false}
                        zoomOnScroll={true}
                        panOnScroll={false}
                        style={{ background: '#fafafa' }}
                    >
                        <Background />
                        <Controls />
                    </ReactFlow>
                    
                    <div style={{
                        position: 'absolute',
                        top: '10px',
                        right: '10px',
                        background: 'rgba(255, 255, 255, 0.95)',
                        padding: '12px',
                        borderRadius: '6px',
                        boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
                        fontSize: '12px',
                        color: '#666',
                        maxWidth: '200px'
                    }}>
                        <div style={{ fontWeight: '600', color: '#333', marginBottom: '4px' }}>
                            {data.Name || '子流程'}
                        </div>
                        <div>节点数量: {data.Cmds?.length || 0}</div>
                        {data.Dispatcher && <div>分发器: {data.Dispatcher}</div>}
                        {data.CmdFailEndCmdName && <div>失败处理: {data.CmdFailEndCmdName}</div>}
                    </div>
                </div>
            );
        };

        /**
         * 主React应用组件
         */
        const WorkflowEditor = () => {
            const [nodes, setNodes, onNodesChange] = useNodesState([]);
            const [edges, setEdges, onEdgesChange] = useEdgesState([]);
            const [selectedElements, setSelectedElements] = useState([]);
            const [showPropertiesPanel, setShowPropertiesPanel] = useState(false);
            const [showSubFlowModal, setShowSubFlowModal] = useState(false);
            const [subFlowData, setSubFlowData] = useState(null);
            const [workflowApp] = useState(() => new WorkflowApp());

            const onConnect = useCallback((params) => {
                const newEdge = workflowApp.edgeManager.createEdge(params);
                setEdges((eds) => addEdge(newEdge, eds));
            }, [workflowApp.edgeManager]);

            const onSelectionChange = useCallback((elements) => {
                setSelectedElements(elements || []);
            }, []);

            const onNodeClick = useCallback((event, node) => {
                if (node.data.Args?.Type === 'SubFlow' && node.data.Cmd) {
                    workflowApp.subFlowManager.handleSubFlowClick(node.data);
                }
            }, [workflowApp.subFlowManager]);

            // 设置UI管理器事件监听
            useEffect(() => {
                workflowApp.uiManager.addEventListener('show-subflow-modal', (eventData) => {
                    setSubFlowData(eventData.data);
                    setShowSubFlowModal(true);
                });
            }, [workflowApp.uiManager]);

            // 文件加载处理
            const handleFileLoad = useCallback(async (file) => {
                try {
                    const text = await file.text();
                    const data = JSON.parse(text);
                    
                    const result = await workflowApp.loadData(data);
                    setNodes(result.nodes);
                    setEdges(result.edges);
                    
                    workflowApp.currentData = { nodes: result.nodes, edges: result.edges };
                    workflowApp.saveToLocalStorage({ nodes: result.nodes, edges: result.edges });
                } catch (error) {
                    workflowApp.uiManager.showMessage('error', '文件加载失败');
                }
            }, [workflowApp]);

            // 初始化时尝试从本地存储加载数据
            useEffect(() => {
                const savedData = workflowApp.loadFromLocalStorage();
                if (savedData && savedData.nodes && savedData.edges) {
                    setNodes(savedData.nodes);
                    setEdges(savedData.edges);
                    workflowApp.currentData = savedData;
                }
            }, [workflowApp]);

            return (
                <div className="workflow-container">
                    {/* 顶部工具栏 */}
                    <div style={{ 
                        height: '60px', 
                        background: '#f5f5f5', 
                        padding: '0 16px',
                        display: 'flex',
                        alignItems: 'center',
                        borderBottom: '1px solid #e8e8e8'
                    }}>
                        <div style={{ fontSize: '18px', fontWeight: '600', color: '#333' }}>
                            工作流程编辑器 (面向对象版本)
                        </div>
                        <div style={{ marginLeft: 'auto', display: 'flex', gap: '8px' }}>
                            <input
                                type="file"
                                accept=".json"
                                onChange={(e) => {
                                    const file = e.target.files[0];
                                    if (file) handleFileLoad(file);
                                }}
                                style={{ display: 'none' }}
                                id="file-input"
                            />
                            <button
                                onClick={() => document.getElementById('file-input').click()}
                                style={{
                                    padding: '6px 12px',
                                    background: '#1890ff',
                                    color: 'white',
                                    border: 'none',
                                    borderRadius: '4px',
                                    cursor: 'pointer'
                                }}
                            >
                                加载文件
                            </button>
                            <button
                                onClick={() => workflowApp.exportData()}
                                style={{
                                    padding: '6px 12px',
                                    background: '#52c41a',
                                    color: 'white',
                                    border: 'none',
                                    borderRadius: '4px',
                                    cursor: 'pointer'
                                }}
                            >
                                导出数据
                            </button>
                            <button
                                onClick={() => setShowPropertiesPanel(!showPropertiesPanel)}
                                style={{
                                    padding: '6px 12px',
                                    background: showPropertiesPanel ? '#fa8c16' : '#d9d9d9',
                                    color: showPropertiesPanel ? 'white' : '#333',
                                    border: 'none',
                                    borderRadius: '4px',
                                    cursor: 'pointer'
                                }}
                            >
                                属性面板
                            </button>
                        </div>
                    </div>

                    {/* 主内容区域 */}
                    <div style={{ display: 'flex', height: 'calc(100vh - 60px)' }}>
                        {/* ReactFlow区域 */}
                        <div style={{ flex: 1, position: 'relative' }}>
                            <ReactFlow
                                nodes={nodes}
                                edges={edges}
                                onNodesChange={onNodesChange}
                                onEdgesChange={onEdgesChange}
                                onConnect={onConnect}
                                onSelectionChange={onSelectionChange}
                                onNodeClick={onNodeClick}
                                nodeTypes={nodeTypes}
                                defaultViewport={{ x: 0, y: 0, zoom: 1.5 }}
                                style={{ background: '#fafafa' }}
                                connectionLineStyle={{ stroke: '#1890ff', strokeWidth: 2 }}
                                deleteKeyCode={['Backspace', 'Delete']}
                            >
                                <Background color="#e1e1e1" gap={20} />
                                <Controls />
                                <MiniMap 
                                    nodeColor={(node) => {
                                        const nodeType = node.data['data-node-type'];
                                        return workflowApp.constants.NODE_COLORS[nodeType] || '#ddd';
                                    }}
                                    style={{
                                        background: 'white',
                                        border: '1px solid #e8e8e8'
                                    }}
                                />
                            </ReactFlow>
                        </div>

                        {/* 属性面板 */}
                        {showPropertiesPanel && (
                            <div style={{
                                width: '300px',
                                background: 'white',
                                borderLeft: '1px solid #e8e8e8',
                                padding: '16px',
                                overflow: 'auto'
                            }}>
                                <h3 style={{ margin: '0 0 16px 0' }}>属性面板</h3>
                                {selectedElements.length > 0 ? (
                                    <div>
                                        <h4>选中元素: {selectedElements.length} 个</h4>
                                        {selectedElements.map((element, index) => (
                                            <div key={index} style={{ marginBottom: '12px', padding: '8px', background: '#f5f5f5', borderRadius: '4px' }}>
                                                <div><strong>类型:</strong> {element.type}</div>
                                                <div><strong>ID:</strong> {element.id}</div>
                                                {element.data?.Name && (
                                                    <div><strong>名称:</strong> {element.data.Name}</div>
                                                )}
                                                {element.data?.Cmd && (
                                                    <div><strong>命令:</strong> {element.data.Cmd}</div>
                                                )}
                                            </div>
                                        ))}
                                    </div>
                                ) : (
                                    <div style={{ color: '#999' }}>请选择节点或连线</div>
                                )}
                            </div>
                        )}
                    </div>

                    {/* SubFlow查看模态框 */}
                    {showSubFlowModal && subFlowData && (
                        <div style={{
                            position: 'fixed',
                            top: 0,
                            left: 0,
                            right: 0,
                            bottom: 0,
                            background: 'rgba(0,0,0,0.5)',
                            display: 'flex',
                            alignItems: 'center',
                            justifyContent: 'center',
                            zIndex: 1000
                        }}>
                            <div style={{
                                background: 'white',
                                width: '90%',
                                height: '90%',
                                borderRadius: '8px',
                                display: 'flex',
                                flexDirection: 'column',
                                overflow: 'hidden'
                            }}>
                                <div style={{
                                    padding: '16px',
                                    borderBottom: '1px solid #e8e8e8',
                                    display: 'flex',
                                    alignItems: 'center',
                                    justifyContent: 'space-between'
                                }}>
                                    <h2 style={{ margin: 0 }}>子流程详情: {subFlowData.Name}</h2>
                                    <button
                                        onClick={() => setShowSubFlowModal(false)}
                                        style={{
                                            background: 'none',
                                            border: 'none',
                                            fontSize: '24px',
                                            cursor: 'pointer',
                                            color: '#999'
                                        }}
                                    >
                                        ×
                                    </button>
                                </div>
                                <div style={{ flex: 1, overflow: 'hidden' }}>
                                    <SubFlowViewer data={subFlowData} />
                                </div>
                            </div>
                        </div>
                    )}
                </div>
            );
        };

        // 渲染应用到DOM
        const root = ReactDOM.createRoot(document.getElementById('root'));
        root.render(React.createElement(WorkflowEditor));
    </script>
</body>
</html>
